/*
 *
 *   Copyright (C) 2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <gdk/gdkkeysyms.h>
#include <urlglib/url_info.h>
#include <urlglib/root_node.h>
#include <urlglib/category_node.h>
#include <urlglib/urlglib_util.h>
#include <urlglib/ug_i18n.h>
#include "main_app_callback.h"
#include "category_chooser.h"
#include "category_dialog.h"
#include "global_dialog.h"
#include "view_setting_dialog.h"
#include "download_importer.h"
#include "download_applying.h"
#include "batch_importer.h"
#include "queue_ctrl.h"

// used in main_app_on_download_start ()
// for gtk_tree_selection_selected_foreach ()
static void selection_start_each (GtkTreeModel *model, GtkTreePath *path,
                                  GtkTreeIter *iter, gpointer data)
{
	DownloadNode* dnode;

	gtk_tree_model_get (model, iter, 0, &dnode, -1);
	download_node_start (dnode);
}

// used in main_app_on_download_stop ()
// for gtk_tree_selection_selected_foreach ()
static void selection_stop_each (GtkTreeModel *model, GtkTreePath *path,
                                 GtkTreeIter *iter, gpointer data)
{
	DownloadNode* dnode;

	gtk_tree_model_get (model, iter, 0, &dnode, -1);
	download_node_stop (dnode);
}

// Handler for Main window ---
gboolean main_app_on_delete_event (GtkWidget* widget, GdkEvent* event, MainApp* app)
{
	GtkWidget* dialog;
	int  response;
	gboolean   old_setting;

	if ( root_node_is_active (app->rnode) ) {
		// disable clipboard monitoring when waiting user response
		old_setting = main_app_monitor_clipboard (app, FALSE);

		dialog = gtk_message_dialog_new (app->window.self,
		                                 GTK_DIALOG_DESTROY_WITH_PARENT,
		                                 GTK_MESSAGE_QUESTION,
		                                 GTK_BUTTONS_YES_NO,
		             _("Some of jobs are working.\nAre you sure to quit?"));
		response = gtk_dialog_run (GTK_DIALOG(dialog));
		gtk_widget_destroy (dialog);

		// restore setting of clipboard monitoring
		main_app_monitor_clipboard (app, old_setting);

		if (response==GTK_RESPONSE_NO)
			return TRUE;
	}

	main_app_save_all (app);
	root_node_stop (app->rnode);
	return FALSE;
}

// Keyboard ---
gboolean main_app_on_key_press_event(GtkWidget* widget, GdkEventKey* event, MainApp* app)
{
	if (event->keyval==GDK_Delete) {
		if (widget == (GtkWidget*)app->window.category_view)
			main_app_on_category_erase (widget, app);
		else
			main_app_on_download_erase (widget, app);
	}
	return FALSE;
}

// File ---
static void main_app_on_import_html_response (GtkDialog *dialog, gint response, gpointer data)
{
	MainApp* app = data;
	gchar* filename;
	gchar* filename_utf8;
//	gchar* path;

	if (response == GTK_RESPONSE_ACCEPT) {
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
//		path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (chooser));
//		path = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
		main_app_import_html (app, filename_utf8);
//		g_message (filename);
		g_free (filename);
		g_free (filename_utf8);
	}
	gtk_widget_destroy (GTK_WIDGET (dialog));
}

void main_app_on_import_html (GtkWidget* widget, MainApp* app)
{
	GtkWidget*  chooser;

	chooser = gtk_file_chooser_dialog_new (_("Import from HTML file (UTF-8 Encoding)"),
	                                       app->window.self,
	                                       GTK_FILE_CHOOSER_ACTION_OPEN,
	                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
	                                       NULL);
	gtk_window_set_transient_for (GTK_WINDOW (chooser), app->window.self);
	gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);

	g_signal_connect (chooser, "response",
	                  G_CALLBACK (main_app_on_import_html_response), app);
	gtk_widget_show (chooser);
}

static void main_app_on_import_url_response (GtkDialog* chooser, gint response, MainApp* app)
{
	gchar* filename;
	gchar* filename_utf8;

	if (response == GTK_RESPONSE_ACCEPT ) {
		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
		filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
		main_app_import_url (app, filename_utf8);
		g_free (filename);
		g_free (filename_utf8);
	}
	gtk_widget_destroy (GTK_WIDGET(chooser));
}

void main_app_on_import_url  (GtkWidget* widget, MainApp* app)
{
	GtkWidget*  chooser;

	chooser = gtk_file_chooser_dialog_new (_("Import URL list from plain text file"),
	                                       app->window.self,
	                                       GTK_FILE_CHOOSER_ACTION_SAVE,
	                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
	                                       NULL);
	gtk_window_set_transient_for (GTK_WINDOW (chooser), app->window.self);
	gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
	g_signal_connect (chooser, "response", G_CALLBACK (main_app_on_import_url_response), app);
	gtk_widget_show (chooser);
}

void main_app_on_export_url (GtkWidget* widget, MainApp* app)
{
	CategoryChooser* cate_chooser;
	GtkWidget*  chooser;
	QueueNode*  qnode;
	gchar* filename;
	gchar* filename_utf8;
	int    response;
	gboolean   old_setting;

	if (app->window.queue_ctrl == NULL)
		return;

	// disable clipboard monitoring when waiting user response
	old_setting = main_app_monitor_clipboard (app, FALSE);

	cate_chooser = category_chooser_new (app->window.self, app->rnode);
	category_chooser_set_title (cate_chooser, _("Export URL list"));
	category_chooser_set_hint (cate_chooser, _("Export selected Category:"));
	category_chooser_set_selected (cate_chooser, app->window.queue_ctrl->qnode);
	response = category_chooser_run (cate_chooser);
	qnode = category_chooser_get_selected (cate_chooser);
	category_chooser_destroy (cate_chooser);

	if (response == GTK_RESPONSE_OK) {
		chooser = gtk_file_chooser_dialog_new (_("Export URL list to plain text file"),
		                                       app->window.self,
		                                       GTK_FILE_CHOOSER_ACTION_SAVE,
		                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
		                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
		                                       NULL);
		gtk_window_set_transient_for (GTK_WINDOW (chooser), app->window.self);
		gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);

		if (gtk_dialog_run (GTK_DIALOG(chooser)) == GTK_RESPONSE_ACCEPT ) {
			filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
			filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
			main_app_export_url (app, filename_utf8, qnode);
			g_free (filename);
			g_free (filename_utf8);
		}
		gtk_widget_destroy (GTK_WIDGET(chooser));
	}

	// restore setting of clipboard monitoring
	main_app_monitor_clipboard (app, old_setting);
}

void main_app_on_save_all  (GtkWidget* widget, MainApp* app)
{
	main_app_save_all (app);
}

// Category ---
void main_app_on_category_selected (GtkTreeView* view, MainApp* app)
{
	GtkTreeModel*     model;
	GtkTreePath*      path;
	GtkTreeIter       iter;
	QueueNode*        qnode;
	QueueCtrl*        qctrl;

	gtk_tree_view_get_cursor (view, &path, NULL);
	if (path == NULL)
		return;

	model = gtk_tree_view_get_model (view);
	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_path_free (path);
	gtk_tree_model_get (model, &iter, 0, &qnode, -1);
	qctrl = g_tree_lookup (app->queue_ctrl_tree, qnode);
	if (qctrl == NULL) {
		qctrl = queue_ctrl_new (qnode);
		g_tree_insert (app->queue_ctrl_tree, qnode, qctrl);
		g_signal_connect (qctrl->view, "key-press-event",
		                  G_CALLBACK (main_app_on_key_press_event), app);
	}

	main_window_set_queue_ctrl (&app->window, qctrl);
}

static void main_app_on_category_new_response (GtkDialog *dlg, gint response, gpointer data)
{
	CategoryDialog* cdialog = data;
	MainApp* app  = cdialog->user_data;
	CategoryNode*   cnode;

	if (response == GTK_RESPONSE_OK) {
		cnode = category_node_new ();
		category_dialog_get (cdialog, cnode);
		root_node_append (app->rnode, cnode);
	}
	category_dialog_destroy (cdialog);
}

void main_app_on_category_new (GtkWidget* widget, MainApp* app)
{
	CategoryDialog* cdialog;

	cdialog = category_dialog_new (app->window.self);
	cdialog->user_data = app;
	category_dialog_set (cdialog, app->rnode->category_default);
	category_dialog_set_title (cdialog, _("Create a new Category"));
	g_signal_connect (cdialog->self, "response",
	                  G_CALLBACK (main_app_on_category_new_response), cdialog);

	gtk_widget_show (cdialog->self);
}

void main_app_on_category_erase (GtkWidget* widget, MainApp* app)
{
	GtkTreeView*      view;
	GtkTreePath*      path;
	GtkWidget*        dialog;
	CategoryNode*     cnode;
	QueueNode*        qnode;
	int        response;
	gboolean   old_setting;

	if (app->window.queue_ctrl == NULL)
		return;
	cnode = app->window.queue_ctrl->cnode;

	// disable clipboard monitoring when waiting user response
	old_setting = main_app_monitor_clipboard (app, FALSE);

	dialog = gtk_message_dialog_new (app->window.self,
	                                 GTK_DIALOG_DESTROY_WITH_PARENT,
	                                 GTK_MESSAGE_QUESTION,
	                                 GTK_BUTTONS_YES_NO,
	     _("This will erase category \"%s\" and it's download.\nAre you sure to erase?"),
	                                 (cnode->name) ? cnode->name : "");
	response = gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);

	// restore setting of clipboard monitoring
	main_app_monitor_clipboard (app, old_setting);

	if (response == GTK_RESPONSE_NO)
		return;

	// Get selected path
	view  = app->window.category_view;
	gtk_tree_view_get_cursor (view, &path, NULL);
	if (path == NULL)
		return;

	// delete all QueueNode and it's QueueCtrl contain in CategoryNode
	qnode = category_node_waiting_queue (cnode);
	main_app_remove_queue_ctrl (app, qnode);
	qnode = category_node_completed_queue (cnode);
	main_app_remove_queue_ctrl (app, qnode);
	qnode = category_node_recycled_queue (cnode);
	main_app_remove_queue_ctrl (app, qnode);

	// Try to change cursor
	if ( category_node_next (cnode) )
		gtk_tree_path_next (path);
	else if ( category_node_prev (cnode) )
		gtk_tree_path_prev (path);
	else {
		gtk_tree_path_free (path);
		path = NULL;
	}

	if (path) {
		gtk_tree_view_set_cursor (view, path, NULL, FALSE);
		gtk_tree_path_free (path);
	} else {
		main_window_set_queue_ctrl (&app->window, NULL);
	}

	category_node_stop (cnode);
	category_node_unlink (cnode);
	category_node_unref (cnode);
}

static void main_app_on_category_prop_response (GtkDialog *dlg, gint response, gpointer data)
{
	CategoryDialog* cdialog = data;
	CategoryNode*   cnode   = cdialog->user_data2;
	MainApp*        app     = cdialog->user_data;

	if (response == GTK_RESPONSE_OK)
		category_dialog_get (cdialog, cnode);
	category_node_unref (cnode);  // ref () at main_app_on_category_prop ()
	category_dialog_destroy (cdialog);
	main_app_refresh (app);
}

void main_app_on_category_prop (GtkWidget* widget, MainApp* app)
{
	CategoryDialog* cdialog;
	CategoryNode*   cnode;

	// Get selected queue/category
	if (app->window.queue_ctrl == NULL)
		return;
	cnode = app->window.queue_ctrl->cnode;
	category_node_ref (cnode);   // unref() at main_app_on_category_prop_response ()

	cdialog = category_dialog_new (app->window.self);
	cdialog->user_data  = app;
	cdialog->user_data2 = cnode;
	category_dialog_set (cdialog, cnode);
	g_signal_connect (cdialog->self, "response",
	                  G_CALLBACK (main_app_on_category_prop_response), cdialog);

	gtk_widget_show (cdialog->self);
}

static void main_app_on_category_default_response (GtkDialog *dlg, gint response, gpointer data)
{
	CategoryDialog* cdialog = data;
	CategoryNode*   cnode   = cdialog->user_data2;
//	MainApp*        app     = cdialog->user_data;

	if (response == GTK_RESPONSE_OK)
		category_dialog_get (cdialog, cnode);
	category_node_unref (cnode);  // ref () at main_app_on_category_default ()
	category_dialog_destroy (cdialog);
}

void main_app_on_category_default (GtkWidget* widget, MainApp* app)
{
	CategoryNode*   cnode;
	CategoryDialog* cdialog;

	cnode = app->rnode->category_default;
	category_node_ref (cnode);   // unref() at main_app_on_category_default_response ()

	cdialog = category_dialog_new (app->window.self);
	cdialog->user_data  = app;
	cdialog->user_data2 = cnode;
	category_dialog_set_title (cdialog, _("Default of new Category"));
	category_dialog_set (cdialog, cnode);
	g_signal_connect (cdialog->self, "response",
	                  G_CALLBACK (main_app_on_category_default_response), cdialog);
	gtk_widget_show (cdialog->self);
}

// Download ---
void main_app_on_download_new (GtkWidget* widget, MainApp* app)
{
	DownloadImporter* dim;
	UrlInfo*      uinfo;
	gchar*        clip_text;
	char*         url;
	int           url_len;

	if (app->window.queue_ctrl == NULL)
		return;

	dim = download_importer_new (app->window.self, app);
	download_dialog_set_category (DOWNLOAD_DIALOG (dim), app->rnode, app->window.queue_ctrl->cnode);
	download_dialog_set_title (DOWNLOAD_DIALOG (dim), _("Create a new Download"));

	// check clipboard
	url = NULL;
	if ( gtk_clipboard_wait_is_text_available (app->clipboard) ) {
		clip_text = gtk_clipboard_wait_for_text (app->clipboard);
		text_get_line_beg (clip_text, &url, &url_len);
		uinfo = url_info_new_len (url, url_len);
		if (uinfo->protocol)
			url = g_strndup (url, url_len);
		else
			url = NULL;
		url_info_free (uinfo);
		g_free (clip_text);
	}

	download_importer_activate_1 (dim, url);
	g_free (url);
}

void main_app_on_download_batch (GtkWidget* widget, MainApp* app)
{
	BatchImporter* bim;

	bim = batch_importer_new (app->window.self, app);
	batch_importer_activate (bim);
}

void main_app_on_download_erase (GtkWidget* widget, MainApp* app)
{
	GList* list;
	GList* list_cur;
	GtkWidget* dialog;
	DownloadNode* dnode;
	QueueNode*    qnode_recycled;

	// check shift key status
	GdkWindow*        gdk_win;
	GdkModifierType   mask;

	gboolean confirmed = FALSE;
	gboolean erase_working = FALSE;
	gboolean moving;
	gboolean old_setting;
	gint     response;

	if (app->window.queue_ctrl == NULL)
		return;

	// check shift key status & current queue
	qnode_recycled = category_node_recycled_queue (app->window.queue_ctrl->cnode);

	gdk_win = gtk_widget_get_parent_window ((GtkWidget*)app->window.queue_ctrl->view);
	gdk_window_get_pointer (gdk_win, NULL, NULL, &mask);

	if (mask & GDK_SHIFT_MASK || qnode_recycled == app->window.queue_ctrl->qnode)
		moving = FALSE;
	else
		moving = TRUE;

	if (moving == FALSE) {
		// notify when job will delete really.
		// disable clipboard monitoring when waiting user response
		old_setting = main_app_monitor_clipboard (app, FALSE);

		dialog = gtk_message_dialog_new (app->window.self,
		                                 GTK_DIALOG_DESTROY_WITH_PARENT,
		                                 GTK_MESSAGE_QUESTION,
		                                 GTK_BUTTONS_YES_NO,
		                _("These job will delete directly.\nAre you sure to delete these?"));
		response = gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);

		// restore setting of clipboard monitoring
		main_app_monitor_clipboard (app, old_setting);
		if (response == GTK_RESPONSE_NO)
			return;
	}

	re_delete:
	list = main_app_get_selected_download (app);
	for (list_cur = list; list_cur; list_cur=list_cur->next) {
		dnode = list_cur->data;

		// need confirm if downloading.
		if (confirmed==FALSE && download_node_is_active (dnode)) {
			confirmed = TRUE;

			// disable clipboard monitoring when waiting user response
			old_setting = main_app_monitor_clipboard (app, FALSE);

			dialog = gtk_message_dialog_new (app->window.self,
			                                 GTK_DIALOG_DESTROY_WITH_PARENT,
			                                 GTK_MESSAGE_QUESTION,
			                                 GTK_BUTTONS_YES_NO,
			     _("Some of selected jobs are working.\nAre you sure to delete these?"));
			response = gtk_dialog_run (GTK_DIALOG (dialog));
			gtk_widget_destroy (dialog);

			// restore setting of clipboard monitoring
			main_app_monitor_clipboard (app, old_setting);

			if (response == GTK_RESPONSE_YES)
				erase_working = TRUE;
			// selected download may changed when confirming.
			g_list_free (list);
			goto re_delete;
		}
		if (download_node_is_active (dnode) && erase_working==FALSE)
			continue;
		download_node_stop (dnode);
		download_node_unlink (dnode);
		if (moving)
			queue_node_prepend (qnode_recycled, dnode);
		else
			download_node_unref (dnode);
	}
	g_list_free (list);
	main_app_refresh (app);
}

void main_app_on_download_prop (GtkWidget* widget, MainApp* app)
{
	DownloadApplying* dapl;
	GList* list;

	list = main_app_get_selected_download (app);
	if (list) {
		list = g_list_reverse (list);
		dapl = download_applying_new (app->window.self, app);
		download_applying_set (dapl, list);
		download_applying_activate (dapl);
	}
}

void main_app_on_download_move_to (GtkWidget* widget, MainApp* app)
{
	CategoryChooser* chooser;
	GList* list;
	GList* list_cur;
	QueueNode*    qnode_src;
	QueueNode*    qnode_dest;
	DownloadNode* dnode;
	gboolean      old_setting;

	if (app->window.queue_ctrl == NULL)
		return;

	// disable clipboard monitoring when waiting user response
	old_setting = main_app_monitor_clipboard (app, FALSE);

	qnode_src = app->window.queue_ctrl->qnode;
	chooser = category_chooser_new (app->window.self, app->rnode);
	category_chooser_set_title (chooser, _("Move"));
	category_chooser_set_hint (chooser, _("Move jobs to the selected Category:"));
	if (category_chooser_run (chooser) == GTK_RESPONSE_OK) {
		qnode_dest = category_chooser_get_selected (chooser);
		if (qnode_src != qnode_dest) {
			list = main_app_get_selected_download (app);
			list = g_list_reverse (list);
			for (list_cur=list; list_cur; list_cur=list_cur->next) {
				dnode = list_cur->data;
				if (queue_node_is_waiting (qnode_dest)==FALSE)
					download_node_stop (dnode);
				download_node_unlink (dnode);
				queue_node_add (qnode_dest, dnode);
			}
			g_list_free (list);
		}
	}
	category_chooser_destroy (chooser);
	main_app_refresh (app);

	// restore setting of clipboard monitoring
	main_app_monitor_clipboard (app, old_setting);
}

void main_app_on_download_move_up (GtkWidget* widget, MainApp* app)
{
	GList* list;
	GList* list_cur;
	QueueNode*    qnode;
	DownloadNode* dnode;
	DownloadNode* dnode_prev;

	if (app->window.queue_ctrl == NULL)
		return;

	qnode = app->window.queue_ctrl->qnode;
	list = main_app_get_selected_download (app);
	app->window.download_moving = TRUE;  // disable selection changed signal
	for (list_cur=g_list_last (list); list_cur; list_cur=list_cur->prev) {
		dnode = list_cur->data;
		dnode_prev = download_node_prev (dnode);
		if (dnode_prev == NULL)
			continue;
		if (list_cur->next && list_cur->next->data == dnode_prev)
			continue;
		download_node_unlink (dnode);
		queue_node_insert_before (qnode, dnode_prev, dnode);
	}
	main_app_set_selected_download (app, list, 1);
	app->window.download_moving = FALSE;  // enable selection changed signal
	g_list_free (list);
}

void main_app_on_download_move_down (GtkWidget* widget, MainApp* app)
{
	GList* list;
	GList* list_cur;
	QueueNode*    qnode;
	DownloadNode* dnode;
	DownloadNode* dnode_next;

	if (app->window.queue_ctrl == NULL)
		return;

	qnode = app->window.queue_ctrl->qnode;
	list = main_app_get_selected_download (app);
	app->window.download_moving = TRUE;  // disable selection changed signal
	for (list_cur = list; list_cur; list_cur=list_cur->next) {
		dnode = list_cur->data;
		dnode_next = download_node_next (dnode);
		if (dnode_next == NULL)
			continue;
		if (list_cur->prev && list_cur->prev->data == dnode_next)
			continue;
		download_node_unlink (dnode);
		queue_node_insert_after (qnode, dnode_next, dnode);
	}
	main_app_set_selected_download (app, list, 1);
	app->window.download_moving = FALSE;  // enable selection changed signal
	g_list_free (list);
}

void main_app_on_download_move_top (GtkWidget* widget, MainApp* app)
{
	GList* list;
	GList* list_cur;
	GtkTreeView*  tview;
	QueueNode*    qnode;
	DownloadNode* dnode;

	if (app->window.queue_ctrl == NULL)
		return;

	// disable selection changed signal
	app->window.download_moving = TRUE;

	// get selected download before unset model
	qnode = app->window.queue_ctrl->qnode;
	list = main_app_get_selected_download (app);

	// Work around : avoid some displayed issue (part 1)
	tview = app->window.queue_ctrl->view;
	gtk_tree_view_set_model (tview, NULL);

	for (list_cur = list; list_cur; list_cur=list_cur->next) {
		dnode = list_cur->data;
		download_node_unlink (dnode);
		queue_node_prepend (qnode, dnode);
	}

	// Work around : avoid some displayed issue (part 2)
	gtk_tree_view_set_model (tview, app->window.queue_ctrl->model);
	main_app_set_selected_download (app, list, -1);
	g_list_free (list);

	// enable selection changed signal
	app->window.download_moving = FALSE;
}

void main_app_on_download_move_bottom (GtkWidget* widget, MainApp* app)
{
	GList* list;
	GList* list_cur;
	QueueNode*    qnode;
	DownloadNode* dnode;
	DownloadNode* dnode_tail;

	if (app->window.queue_ctrl == NULL)
		return;

	qnode = app->window.queue_ctrl->qnode;
	dnode_tail = queue_node_last_download (qnode);
	list = main_app_get_selected_download (app);
	app->window.download_moving = TRUE;  // disable selection changed signal
	for (list_cur = list; list_cur; list_cur=list_cur->next) {
		dnode = list_cur->data;
		if (dnode == dnode_tail) {
			dnode_tail = download_node_prev (dnode_tail);
			if (dnode_tail)
				continue;
			break;
		}
		download_node_unlink (dnode);
		queue_node_insert_after (qnode, dnode_tail, dnode);
	}
	main_app_set_selected_download (app, list, 1);
	app->window.download_moving = FALSE;  // enable selection changed signal
	g_list_free (list);
}

void main_app_on_download_start (GtkWidget* widget, MainApp* app)
{
	GtkTreeSelection* selection;

	if (app->window.queue_ctrl == NULL)
		return;

	selection = gtk_tree_view_get_selection (app->window.queue_ctrl->view);
	gtk_tree_selection_selected_foreach (selection, selection_start_each, NULL);
	main_app_refresh (app);
	main_app_job_changed (app);
}

void main_app_on_download_stop (GtkWidget* widget, MainApp* app)
{
	GtkTreeSelection* selection;

	if (app->window.queue_ctrl == NULL)
		return;

	selection = gtk_tree_view_get_selection (app->window.queue_ctrl->view);
	gtk_tree_selection_selected_foreach (selection, selection_stop_each, NULL);
	main_app_refresh (app);
//	main_app_job_changed (app);
}

// Global ---
void main_app_on_global_start (GtkWidget* widget, MainApp* app)
{
	root_node_start_paused (app->rnode);
	main_app_set_queuing (app, TRUE);
	main_app_refresh (app);
}

void main_app_on_global_pause (GtkWidget* widget, MainApp* app)
{
	main_app_set_queuing (app, FALSE);
	root_node_stop (app->rnode);
	main_app_refresh (app);
}

void main_app_on_global_set (GtkWidget* widget, MainApp* app)
{
	GlobalDialog* gd;
	const char* err_str;
	int   err_len;

	gd = global_dialog_new (app->window.self);
	global_dialog_set_active_category (gd, app->rnode->active_max);
	global_dialog_set_monitoring (gd, app->clipboard_monitor);
	global_dialog_set_type (gd, app->clipboard_pattern);

	if (gtk_dialog_run (gd->self) == GTK_RESPONSE_OK) {
		app->rnode->active_max = global_dialog_get_active_category (gd);
		app->clipboard_monitor = global_dialog_get_monitoring (gd);
		str_replace_no_crlf (&app->clipboard_pattern, global_dialog_get_type (gd), -1);
		pcre_free (app->pcre_regexp);
		app->pcre_regexp = pcre_compile (app->clipboard_pattern, PCRE_CASELESS | PCRE_UTF8,
		                                 &err_str, &err_len, NULL);
	}

	global_dialog_destroy (gd);
}

// View ---
static void main_app_on_view_column_response (GtkDialog *dlg, gint response, gpointer data)
{
	ViewSettingDialog* vsd = data;
	MainApp* app = vsd->user_data;

	if (response == GTK_RESPONSE_OK)
		view_setting_dialog_get (vsd, &app->window.view_setting);
	view_setting_dialog_destroy (vsd);

	if (app->window.queue_ctrl)
		queue_ctrl_apply_setting (app->window.queue_ctrl, &app->window.view_setting);
	main_window_message_area_refresh (&app->window);
}

void main_app_on_view_column_set (GtkWidget* widget, MainApp* app)
{
	ViewSettingDialog* vsd;

	vsd = view_setting_dialog_new (app->window.self);
	vsd->user_data = app;
	g_signal_connect (vsd->self, "response",
	                  G_CALLBACK (main_app_on_view_column_response), vsd);

	view_setting_dialog_set (vsd, &app->window.view_setting);
	view_setting_dialog_activate (vsd);
}

void main_app_on_view_msg_item_set (GtkWidget* widget, MainApp* app)
{
	ViewSettingDialog* vsd;

	vsd = view_setting_dialog_new (app->window.self);
	vsd->user_data = app;
	g_signal_connect (vsd->self, "response",
	                  G_CALLBACK (main_app_on_view_column_response), vsd);

	view_setting_dialog_set (vsd, &app->window.view_setting);
	view_setting_dialog_set_page (vsd, 1);
	view_setting_dialog_activate (vsd);
}

// About
void main_app_on_about (GtkWidget* widget, MainApp* app)
{
	GtkWidget* dialog;
	char* message;

	message = g_strdup_printf ("Urlgfe 1.0.2\n\n%s\n\n%s",
	                           _("Authors: Raymond Huang"),
	                           _("Translator: Raymond Huang"));

	dialog = gtk_message_dialog_new (app->window.self,
	                                 GTK_DIALOG_DESTROY_WITH_PARENT,
	                                 GTK_MESSAGE_INFO,
	                                 GTK_BUTTONS_OK,
	                                 message);
	g_free (message);
	gtk_window_set_transient_for (GTK_WINDOW (dialog), app->window.self);
	g_signal_connect (dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
	gtk_widget_show (dialog);
}

